{
 "cells": [
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-27T08:22:44.517635Z",
     "start_time": "2025-02-27T08:22:41.176813Z"
    }
   },
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "from tqdm.auto import tqdm\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, torch:\n",
    "    print(module.__name__, module.__version__)\n",
    "    \n",
    "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
    "print(device)\n",
    "\n",
    "seed = 42\n"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sys.version_info(major=3, minor=12, micro=3, releaselevel='final', serial=0)\n",
      "matplotlib 3.10.0\n",
      "numpy 2.0.2\n",
      "pandas 2.2.3\n",
      "sklearn 1.6.1\n",
      "torch 2.6.0+cu126\n",
      "cuda:0\n"
     ]
    }
   ],
   "execution_count": 1
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 数据准备\n",
    "\n",
    "```shell\n",
    "$ tree -L 2 archive \n",
    "archive\n",
    "├── monkey_labels.txt\n",
    "├── training\n",
    "│   ├── n0\n",
    "│   ├── n1\n",
    "│   ├── n2\n",
    "│   ├── n3\n",
    "│   ├── n4\n",
    "│   ├── n5\n",
    "│   ├── n6\n",
    "│   ├── n7\n",
    "│   ├── n8\n",
    "│   └── n9\n",
    "└── validation\n",
    "    ├── n0\n",
    "    ├── n1\n",
    "    ├── n2\n",
    "    ├── n3\n",
    "    ├── n4\n",
    "    ├── n5\n",
    "    ├── n6\n",
    "    ├── n7\n",
    "    ├── n8\n",
    "    └── n9\n",
    "\n",
    "22 directories, 1 file\n",
    "```"
   ]
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-27T08:26:33.012801Z",
     "start_time": "2025-02-27T08:26:32.984243Z"
    }
   },
   "cell_type": "code",
   "source": "!tree archive ",
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "卷 工作 的文件夹 PATH 列表\n",
      "卷序列号为 00000090 3183:6120\n",
      "D:\\PYTHON\\PYTHON_CODE\\PYTHON_CODE2025\\DAY23\\ARCHIVE\n",
      "├─training\n",
      "│  ├─n0\n",
      "│  ├─n1\n",
      "│  ├─n2\n",
      "│  ├─n3\n",
      "│  ├─n4\n",
      "│  ├─n5\n",
      "│  ├─n6\n",
      "│  ├─n7\n",
      "│  ├─n8\n",
      "│  └─n9\n",
      "└─validation\n",
      "    ├─n0\n",
      "    ├─n1\n",
      "    ├─n2\n",
      "    ├─n3\n",
      "    ├─n4\n",
      "    ├─n5\n",
      "    ├─n6\n",
      "    ├─n7\n",
      "    ├─n8\n",
      "    └─n9\n"
     ]
    }
   ],
   "execution_count": 5
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-27T08:26:23.257410Z",
     "start_time": "2025-02-27T08:26:23.247414Z"
    }
   },
   "source": [
    "from torchvision import datasets\n",
    "from torchvision.transforms import ToTensor, Resize, Compose, ConvertImageDtype, Normalize\n",
    "\n",
    "\n",
    "from pathlib import Path\n",
    "\n",
    "DATA_DIR = Path(\"./archive/\")\n",
    "\n",
    "class MonkeyDataset(datasets.ImageFolder):\n",
    "    def __init__(self, mode, transform=None):\n",
    "        if mode == \"train\":\n",
    "            root = DATA_DIR / \"training\"\n",
    "        elif mode == \"val\":\n",
    "            root = DATA_DIR / \"validation\"\n",
    "        else:\n",
    "            raise ValueError(\"mode should be one of the following: train, val, but got {}\".format(mode))\n",
    "        super().__init__(root, transform) # 调用父类init方法\n",
    "        # self.imgs = self.samples # self.samples里边是图片路径及标签 [(path, label), (path, label),...]\n",
    "        self.targets = [s[1] for s in self.samples] # 标签取出来\n",
    "\n",
    "# 预先设定的图片尺寸\n",
    "img_h, img_w = 128, 128\n",
    "transform = Compose([\n",
    "    Resize((img_h, img_w)), # 图片缩放\n",
    "    ToTensor(),\n",
    "    # 预先统计的\n",
    "    Normalize([0.4363, 0.4328, 0.3291], [0.2085, 0.2032, 0.1988]),\n",
    "    ConvertImageDtype(torch.float), # 转换为float类型\n",
    "]) #数据预处理\n",
    "\n",
    "\n",
    "train_ds = MonkeyDataset(\"train\", transform=transform)\n",
    "val_ds = MonkeyDataset(\"val\", transform=transform)\n",
    "\n",
    "print(\"load {} images from training dataset\".format(len(train_ds)))\n",
    "print(\"load {} images from validation dataset\".format(len(val_ds)))"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "load 1097 images from training dataset\n",
      "load 272 images from validation dataset\n"
     ]
    }
   ],
   "execution_count": 4
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-27T08:29:18.828443Z",
     "start_time": "2025-02-27T08:29:18.823426Z"
    }
   },
   "source": [
    "# 数据类别\n",
    "train_ds.classes"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['n0', 'n1', 'n2', 'n3', 'n4', 'n5', 'n6', 'n7', 'n8', 'n9']"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 6
  },
  {
   "cell_type": "code",
   "source": [
    "train_ds.class_to_idx"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-27T08:29:19.464206Z",
     "start_time": "2025-02-27T08:29:19.459848Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "{'n0': 0,\n",
       " 'n1': 1,\n",
       " 'n2': 2,\n",
       " 'n3': 3,\n",
       " 'n4': 4,\n",
       " 'n5': 5,\n",
       " 'n6': 6,\n",
       " 'n7': 7,\n",
       " 'n8': 8,\n",
       " 'n9': 9}"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 7
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-27T08:29:46.744044Z",
     "start_time": "2025-02-27T08:29:46.740278Z"
    }
   },
   "source": [
    "# # 图片路径 及 标签\n",
    "i=0\n",
    "for fpath, label in train_ds.samples:\n",
    "    print(fpath, label)\n",
    "    i += 1\n",
    "    if i == 10:\n",
    "        break\n",
    "#\n",
    "# #这个和之前的dataset完全一致\n",
    "# for img, label in train_ds:\n",
    "#     # c, h, w  label\n",
    "#     print(img, label)\n",
    "#     break"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "archive\\training\\n0\\n0018.jpg 0\n",
      "archive\\training\\n0\\n0019.jpg 0\n",
      "archive\\training\\n0\\n0020.jpg 0\n",
      "archive\\training\\n0\\n0021.jpg 0\n",
      "archive\\training\\n0\\n0022.jpg 0\n",
      "archive\\training\\n0\\n0023.jpg 0\n",
      "archive\\training\\n0\\n0024.jpg 0\n",
      "archive\\training\\n0\\n0025.jpg 0\n",
      "archive\\training\\n0\\n0026.jpg 0\n",
      "archive\\training\\n0\\n0027.jpg 0\n"
     ]
    }
   ],
   "execution_count": 8
  },
  {
   "cell_type": "code",
   "source": [
    "#因为有3通道，所有有3个mean和std\n",
    "def cal_mean_std(ds):\n",
    "    mean = 0.\n",
    "    std = 0.\n",
    "    for img, _ in ds:\n",
    "        mean += img[:, :, :].mean(dim=(1, 2))\n",
    "        std += img[:, :, :].std(dim=(1, 2))\n",
    "    mean /= len(ds)\n",
    "    std /= len(ds)\n",
    "    return mean, std\n",
    "\n",
    "# 经过 normalize 后 均值为0，方差为1\n",
    "# print(cal_mean_std(train_ds))"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-27T08:30:02.718453Z",
     "start_time": "2025-02-27T08:30:02.714939Z"
    }
   },
   "outputs": [],
   "execution_count": 9
  },
  {
   "cell_type": "code",
   "source": [
    "# def cal_mean_std(ds):\n",
    "#     mean = 0.\n",
    "#     std = 0.\n",
    "#     for img, _ in ds:\n",
    "#         mean += img[1:2, :, :].mean(dim=(1, 2))\n",
    "#         std += img[1:2, :, :].std(dim=(1, 2))\n",
    "#     mean /= len(ds)\n",
    "#     std /= len(ds)\n",
    "#     return mean, std\n",
    "#\n",
    "# # 经过 normalize 后 均值为0，方差为1\n",
    "# print(cal_mean_std(train_ds))"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-27T08:30:59.788601Z",
     "start_time": "2025-02-27T08:30:59.786004Z"
    }
   },
   "outputs": [],
   "execution_count": 10
  },
  {
   "cell_type": "code",
   "source": [
    "def cal_mean_std(ds):\n",
    "    mean = 0.\n",
    "    std = 0.\n",
    "    for img, _ in ds:\n",
    "        mean += img[2:3, :, :].mean(dim=(1, 2))\n",
    "        std += img[2:3, :, :].std(dim=(1, 2))\n",
    "    mean /= len(ds)\n",
    "    std /= len(ds)\n",
    "    return mean, std\n",
    "\n",
    "# 经过 normalize 后 均值为0，方差为1\n",
    "print(cal_mean_std(train_ds))"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2025-02-27T08:39:32.588062Z",
     "start_time": "2025-02-27T08:39:20.230623Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(tensor([-6.5391e-07]), tensor([1.0002]))\n"
     ]
    }
   ],
   "execution_count": 16
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-27T08:39:10.971016Z",
     "start_time": "2025-02-27T08:38:53.756173Z"
    }
   },
   "source": [
    "# 遍历train_ds得到每张图片，计算每个通道的均值和方差\n",
    "def cal_mean_std(ds):\n",
    "    mean = 0.\n",
    "    std = 0.\n",
    "    for img, _ in ds:\n",
    "        mean += img.mean(dim=(1, 2)) #dim=(1, 2)表示计算均值后，宽和高消除（把宽和高所有的像素加起来，再除以总数）\n",
    "        std += img.std(dim=(1, 2))\n",
    "    mean /= len(ds)\n",
    "    std /= len(ds)\n",
    "    return mean, std\n",
    "\n",
    "# 经过 normalize 后 均值为0，方差为1\n",
    "print(cal_mean_std(train_ds))"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(tensor([ 1.5299e-04,  3.6267e-05, -6.5391e-07]), tensor([0.9999, 0.9999, 1.0002]))\n"
     ]
    }
   ],
   "execution_count": 15
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-27T08:31:13.754437Z",
     "start_time": "2025-02-27T08:31:13.750967Z"
    }
   },
   "source": [
    "import torch.nn as nn\n",
    "from torch.utils.data.dataloader import DataLoader    \n",
    "\n",
    "batch_size = 64\n",
    "# 从数据集到dataloader，num_workers参数不能加，否则会报错\n",
    "# https://github.com/pytorch/pytorch/issues/59438\n",
    "train_loader = DataLoader(train_ds, batch_size=batch_size, shuffle=True)\n",
    "val_loader = DataLoader(val_ds, batch_size=batch_size, shuffle=False)"
   ],
   "outputs": [],
   "execution_count": 13
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-27T08:31:15.858871Z",
     "start_time": "2025-02-27T08:31:14.272866Z"
    }
   },
   "source": [
    "for imgs, labels in train_loader:\n",
    "    print(imgs.shape)\n",
    "    print(labels.shape)\n",
    "    break"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "torch.Size([64, 3, 128, 128])\n",
      "torch.Size([64])\n"
     ]
    }
   ],
   "execution_count": 14
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 定义模型"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-27T08:50:19.925784Z",
     "start_time": "2025-02-27T08:50:19.884445Z"
    }
   },
   "source": [
    "\n",
    "class CNN(nn.Module):\n",
    "    def __init__(self, num_classes=10, activation=\"relu\"):\n",
    "        super(CNN, self).__init__()\n",
    "        self.activation = F.relu if activation == \"relu\" else F.selu\n",
    "        self.conv1 = nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, padding=\"same\")\n",
    "        self.conv2 = nn.Conv2d(in_channels=32, out_channels=32, kernel_size=3, padding=\"same\")\n",
    "        self.pool = nn.MaxPool2d(2, 2)\n",
    "        self.conv3 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, padding=\"same\")\n",
    "        self.conv4 = nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, padding=\"same\")\n",
    "        self.conv5 = nn.Conv2d(in_channels=64, out_channels=128, kernel_size=3, padding=\"same\")\n",
    "        self.conv6 = nn.Conv2d(in_channels=128, out_channels=128, kernel_size=3, padding=\"same\")\n",
    "        self.flatten = nn.Flatten()\n",
    "        # input shape is (3, 128, 128) so the flatten output shape is 128 * 16 * 16\n",
    "        self.fc1 = nn.Linear(128 * 16 * 16, 128)\n",
    "        self.fc2 = nn.Linear(128, num_classes)\n",
    "        \n",
    "        self.init_weights()\n",
    "        \n",
    "    def init_weights(self):\n",
    "        \"\"\"使用 xavier 均匀分布来初始化全连接层、卷积层的权重 W\"\"\"\n",
    "        for m in self.modules():\n",
    "            if isinstance(m, (nn.Linear, nn.Conv2d)):\n",
    "                nn.init.xavier_uniform_(m.weight)\n",
    "                nn.init.zeros_(m.bias)\n",
    "        \n",
    "    def forward(self, x):\n",
    "        act = self.activation\n",
    "        x = self.pool(act(self.conv2(act(self.conv1(x)))))\n",
    "        x = self.pool(act(self.conv4(act(self.conv3(x)))))\n",
    "        x = self.pool(act(self.conv6(act(self.conv5(x)))))\n",
    "        x = self.flatten(x)\n",
    "        x = act(self.fc1(x))\n",
    "        x = self.fc2(x)\n",
    "        return x\n",
    "    \n",
    "\n",
    "for idx, (key, value) in enumerate(CNN().named_parameters()):\n",
    "    print(f\"{key}\\tparamerters num: {np.prod(value.shape)}\")\n"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "conv1.weight\tparamerters num: 864\n",
      "conv1.bias\tparamerters num: 32\n",
      "conv2.weight\tparamerters num: 9216\n",
      "conv2.bias\tparamerters num: 32\n",
      "conv3.weight\tparamerters num: 18432\n",
      "conv3.bias\tparamerters num: 64\n",
      "conv4.weight\tparamerters num: 36864\n",
      "conv4.bias\tparamerters num: 64\n",
      "conv5.weight\tparamerters num: 73728\n",
      "conv5.bias\tparamerters num: 128\n",
      "conv6.weight\tparamerters num: 147456\n",
      "conv6.bias\tparamerters num: 128\n",
      "fc1.weight\tparamerters num: 4194304\n",
      "fc1.bias\tparamerters num: 128\n",
      "fc2.weight\tparamerters num: 1280\n",
      "fc2.bias\tparamerters num: 10\n"
     ]
    }
   ],
   "execution_count": 17
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-27T08:50:20.809175Z",
     "start_time": "2025-02-27T08:50:20.773180Z"
    }
   },
   "cell_type": "code",
   "source": [
    "def count_parameters(model): #计算模型总参数量\n",
    "    return sum(p.numel() for p in model.parameters() if p.requires_grad)\n",
    "count_parameters(CNN())"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4482730"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 18
  },
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-27T08:50:21.478654Z",
     "start_time": "2025-02-27T08:50:21.475101Z"
    }
   },
   "cell_type": "code",
   "source": "128*16*16*128",
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4194304"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "execution_count": 19
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 训练\n",
    "\n",
    "pytorch的训练需要自行实现，包括\n",
    "1. 定义损失函数\n",
    "2. 定义优化器\n",
    "3. 定义训练步\n",
    "4. 训练"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-27T08:50:22.366733Z",
     "start_time": "2025-02-27T08:50:22.312002Z"
    }
   },
   "source": [
    "from sklearn.metrics import accuracy_score\n",
    "\n",
    "@torch.no_grad()\n",
    "def evaluating(model, dataloader, loss_fct):\n",
    "    loss_list = []\n",
    "    pred_list = []\n",
    "    label_list = []\n",
    "    for datas, labels in dataloader:\n",
    "        datas = datas.to(device)\n",
    "        labels = labels.to(device)\n",
    "        # 前向计算\n",
    "        logits = model(datas)\n",
    "        loss = loss_fct(logits, labels)         # 验证集损失\n",
    "        loss_list.append(loss.item())\n",
    "        \n",
    "        preds = logits.argmax(axis=-1)    # 验证集预测\n",
    "        pred_list.extend(preds.cpu().numpy().tolist())\n",
    "        label_list.extend(labels.cpu().numpy().tolist())\n",
    "        \n",
    "    acc = accuracy_score(label_list, pred_list)\n",
    "    return np.mean(loss_list), acc\n"
   ],
   "outputs": [],
   "execution_count": 20
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### TensorBoard 可视化\n",
    "\n",
    "\n",
    "训练过程中可以使用如下命令启动tensorboard服务。\n",
    "\n",
    "```shell\n",
    "tensorboard \\\n",
    "    --logdir=runs \\     # log 存放路径\n",
    "    --host 0.0.0.0 \\    # ip\n",
    "    --port 8848         # 端口\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-27T08:50:26.984691Z",
     "start_time": "2025-02-27T08:50:23.421888Z"
    }
   },
   "source": [
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "\n",
    "class TensorBoardCallback:\n",
    "    def __init__(self, log_dir, flush_secs=10):\n",
    "        \"\"\"\n",
    "        Args:\n",
    "            log_dir (str): dir to write log.\n",
    "            flush_secs (int, optional): write to dsk each flush_secs seconds. Defaults to 10.\n",
    "        \"\"\"\n",
    "        self.writer = SummaryWriter(log_dir=log_dir, flush_secs=flush_secs)\n",
    "\n",
    "    def draw_model(self, model, input_shape):\n",
    "        self.writer.add_graph(model, input_to_model=torch.randn(input_shape))\n",
    "        \n",
    "    def add_loss_scalars(self, step, loss, val_loss):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/loss\", \n",
    "            tag_scalar_dict={\"loss\": loss, \"val_loss\": val_loss},\n",
    "            global_step=step,\n",
    "            )\n",
    "        \n",
    "    def add_acc_scalars(self, step, acc, val_acc):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/accuracy\",\n",
    "            tag_scalar_dict={\"accuracy\": acc, \"val_accuracy\": val_acc},\n",
    "            global_step=step,\n",
    "        )\n",
    "        \n",
    "    def add_lr_scalars(self, step, learning_rate):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/learning_rate\",\n",
    "            tag_scalar_dict={\"learning_rate\": learning_rate},\n",
    "            global_step=step,\n",
    "            \n",
    "        )\n",
    "    \n",
    "    def __call__(self, step, **kwargs):\n",
    "        # add loss\n",
    "        loss = kwargs.pop(\"loss\", None)\n",
    "        val_loss = kwargs.pop(\"val_loss\", None)\n",
    "        if loss is not None and val_loss is not None:\n",
    "            self.add_loss_scalars(step, loss, val_loss)\n",
    "        # add acc\n",
    "        acc = kwargs.pop(\"acc\", None)\n",
    "        val_acc = kwargs.pop(\"val_acc\", None)\n",
    "        if acc is not None and val_acc is not None:\n",
    "            self.add_acc_scalars(step, acc, val_acc)\n",
    "        # add lr\n",
    "        learning_rate = kwargs.pop(\"lr\", None)\n",
    "        if learning_rate is not None:\n",
    "            self.add_lr_scalars(step, learning_rate)\n"
   ],
   "outputs": [],
   "execution_count": 21
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Save Best\n"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-27T08:50:31.906853Z",
     "start_time": "2025-02-27T08:50:31.901968Z"
    }
   },
   "source": [
    "class SaveCheckpointsCallback:\n",
    "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
    "        \"\"\"\n",
    "        Save checkpoints each save_epoch epoch. \n",
    "        We save checkpoint by epoch in this implementation.\n",
    "        Usually, training scripts with pytorch evaluating model and save checkpoint by step.\n",
    "\n",
    "        Args:\n",
    "            save_dir (str): dir to save checkpoint\n",
    "            save_epoch (int, optional): the frequency to save checkpoint. Defaults to 1.\n",
    "            save_best_only (bool, optional): If True, only save the best model or save each model at every epoch.\n",
    "        \"\"\"\n",
    "        self.save_dir = save_dir\n",
    "        self.save_step = save_step\n",
    "        self.save_best_only = save_best_only\n",
    "        self.best_metrics = -1\n",
    "        \n",
    "        # mkdir\n",
    "        if not os.path.exists(self.save_dir):\n",
    "            os.mkdir(self.save_dir)\n",
    "        \n",
    "    def __call__(self, step, state_dict, metric=None):\n",
    "        if step % self.save_step > 0:\n",
    "            return\n",
    "        \n",
    "        if self.save_best_only:\n",
    "            assert metric is not None\n",
    "            if metric >= self.best_metrics:\n",
    "                # save checkpoints\n",
    "                torch.save(state_dict, os.path.join(self.save_dir, \"best.ckpt\"))\n",
    "                # update best metrics\n",
    "                self.best_metrics = metric\n",
    "        else:\n",
    "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))\n",
    "\n"
   ],
   "outputs": [],
   "execution_count": 22
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Early Stop"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-27T08:50:32.969435Z",
     "start_time": "2025-02-27T08:50:32.965670Z"
    }
   },
   "source": [
    "class EarlyStopCallback:\n",
    "    def __init__(self, patience=5, min_delta=0.01):\n",
    "        \"\"\"\n",
    "\n",
    "        Args:\n",
    "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
    "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute \n",
    "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
    "        \"\"\"\n",
    "        self.patience = patience\n",
    "        self.min_delta = min_delta\n",
    "        self.best_metric = -1\n",
    "        self.counter = 0\n",
    "        \n",
    "    def __call__(self, metric):\n",
    "        if metric >= self.best_metric + self.min_delta:\n",
    "            # update best metric\n",
    "            self.best_metric = metric\n",
    "            # reset counter \n",
    "            self.counter = 0\n",
    "        else: \n",
    "            self.counter += 1\n",
    "            \n",
    "    @property\n",
    "    def early_stop(self):\n",
    "        return self.counter >= self.patience\n"
   ],
   "outputs": [],
   "execution_count": 23
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-27T08:56:37.284651Z",
     "start_time": "2025-02-27T08:50:33.494112Z"
    }
   },
   "source": [
    "# 训练\n",
    "def training(\n",
    "    model, \n",
    "    train_loader, \n",
    "    val_loader, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=None,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500,\n",
    "    ):\n",
    "    record_dict = {\n",
    "        \"train\": [],\n",
    "        \"val\": []\n",
    "    }\n",
    "    \n",
    "    global_step = 0\n",
    "    model.train()\n",
    "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
    "        for epoch_id in range(epoch):\n",
    "            # training\n",
    "            for datas, labels in train_loader:\n",
    "                datas = datas.to(device)\n",
    "                labels = labels.to(device)\n",
    "                # 梯度清空\n",
    "                optimizer.zero_grad()\n",
    "                # 模型前向计算\n",
    "                logits = model(datas)\n",
    "                # 计算损失\n",
    "                loss = loss_fct(logits, labels)\n",
    "                # 梯度回传\n",
    "                loss.backward()\n",
    "                # 调整优化器，包括学习率的变动等\n",
    "                optimizer.step()\n",
    "                preds = logits.argmax(axis=-1)\n",
    "            \n",
    "                acc = accuracy_score(labels.cpu().numpy(), preds.cpu().numpy())    \n",
    "                loss = loss.cpu().item()\n",
    "                # record\n",
    "                \n",
    "                record_dict[\"train\"].append({\n",
    "                    \"loss\": loss, \"acc\": acc, \"step\": global_step\n",
    "                })\n",
    "                \n",
    "                # evaluating\n",
    "                if global_step % eval_step == 0:\n",
    "                    model.eval()\n",
    "                    val_loss, val_acc = evaluating(model, val_loader, loss_fct)\n",
    "                    record_dict[\"val\"].append({\n",
    "                        \"loss\": val_loss, \"acc\": val_acc, \"step\": global_step\n",
    "                    })\n",
    "                    model.train()\n",
    "                    \n",
    "                    # 1. 使用 tensorboard 可视化\n",
    "                    if tensorboard_callback is not None:\n",
    "                        tensorboard_callback(\n",
    "                            global_step, \n",
    "                            loss=loss, val_loss=val_loss,\n",
    "                            acc=acc, val_acc=val_acc,\n",
    "                            lr=optimizer.param_groups[0][\"lr\"],\n",
    "                            )\n",
    "                \n",
    "                    # 2. 保存模型权重 save model checkpoint\n",
    "                    if save_ckpt_callback is not None:\n",
    "                        save_ckpt_callback(global_step, model.state_dict(), metric=val_acc)\n",
    "\n",
    "                    # 3. 早停 Early Stop\n",
    "                    if early_stop_callback is not None:\n",
    "                        early_stop_callback(val_acc)\n",
    "                        if early_stop_callback.early_stop:\n",
    "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
    "                            return record_dict\n",
    "                    \n",
    "                # udate step\n",
    "                global_step += 1\n",
    "                pbar.update(1)\n",
    "                pbar.set_postfix({\"epoch\": epoch_id})\n",
    "        \n",
    "    return record_dict\n",
    "        \n",
    "\n",
    "epoch = 20\n",
    "\n",
    "activation = \"relu\"\n",
    "model = CNN(num_classes=10, activation=activation)\n",
    "\n",
    "# 1. 定义损失函数 采用交叉熵损失\n",
    "loss_fct = nn.CrossEntropyLoss()\n",
    "# 2. 定义优化器 采用 adam\n",
    "# Optimizers specified in the torch.optim package\n",
    "optimizer = torch.optim.Adam(model.parameters(), lr=0.001, eps=1e-7)\n",
    "\n",
    "# 1. tensorboard 可视化\n",
    "if not os.path.exists(\"runs\"):\n",
    "    os.mkdir(\"runs\")\n",
    "tensorboard_callback = TensorBoardCallback(f\"runs/monkeys-cnn-{activation}\")\n",
    "tensorboard_callback.draw_model(model, [1, 3, img_h, img_w])\n",
    "# 2. save best\n",
    "if not os.path.exists(\"checkpoints\"):\n",
    "    os.makedirs(\"checkpoints\")\n",
    "save_ckpt_callback = SaveCheckpointsCallback(f\"checkpoints/monkeys-cnn-{activation}\", save_step=len(train_loader), save_best_only=True)\n",
    "# 3. early stop\n",
    "early_stop_callback = EarlyStopCallback(patience=5)\n",
    "\n",
    "model = model.to(device)\n",
    "record = training(\n",
    "    model, \n",
    "    train_loader, \n",
    "    val_loader, \n",
    "    epoch, \n",
    "    loss_fct, \n",
    "    optimizer, \n",
    "    tensorboard_callback=tensorboard_callback,\n",
    "    save_ckpt_callback=save_ckpt_callback,\n",
    "    early_stop_callback=early_stop_callback,\n",
    "    eval_step=len(train_loader)\n",
    "    )"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "  0%|          | 0/360 [00:00<?, ?it/s]"
      ],
      "application/vnd.jupyter.widget-view+json": {
       "version_major": 2,
       "version_minor": 0,
       "model_id": "8dbfbf022e4c47c79eebe1e344b9f5f2"
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Early stop at epoch 18 / global_step 324\n"
     ]
    }
   ],
   "execution_count": 24
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-27T08:56:43.523608Z",
     "start_time": "2025-02-27T08:56:43.385831Z"
    }
   },
   "source": [
    "#画线要注意的是损失是不一定在零到1之间的\n",
    "def plot_learning_curves(record_dict, sample_step=500):\n",
    "    # build DataFrame\n",
    "    train_df = pd.DataFrame(record_dict[\"train\"]).set_index(\"step\").iloc[::sample_step]\n",
    "    val_df = pd.DataFrame(record_dict[\"val\"]).set_index(\"step\")\n",
    "\n",
    "    # plot\n",
    "    fig_num = len(train_df.columns)\n",
    "    fig, axs = plt.subplots(1, fig_num, figsize=(5 * fig_num, 5))\n",
    "    for idx, item in enumerate(train_df.columns):    \n",
    "        axs[idx].plot(train_df.index, train_df[item], label=f\"train_{item}\")\n",
    "        axs[idx].plot(val_df.index, val_df[item], label=f\"val_{item}\")\n",
    "        axs[idx].grid()\n",
    "        axs[idx].legend()\n",
    "        # axs[idx].set_xticks(range(0, train_df.index[-1], 5000))\n",
    "        # axs[idx].set_xticklabels(map(lambda x: f\"{int(x/1000)}k\", range(0, train_df.index[-1], 5000)))\n",
    "        axs[idx].set_xlabel(\"step\")\n",
    "    \n",
    "    plt.show()\n",
    "\n",
    "plot_learning_curves(record, sample_step=10)  #横坐标是 steps"
   ],
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ],
      "image/png": "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"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "execution_count": 25
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 评估"
   ]
  },
  {
   "cell_type": "code",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-02-27T08:56:50.403205Z",
     "start_time": "2025-02-27T08:56:45.967855Z"
    }
   },
   "source": [
    "# dataload for evaluating\n",
    "\n",
    "# load checkpoints\n",
    "model.load_state_dict(torch.load(f\"checkpoints/monkeys-cnn-{activation}/best.ckpt\", map_location=\"cpu\"))\n",
    "\n",
    "model.eval()\n",
    "loss, acc = evaluating(model, val_loader, loss_fct)\n",
    "print(f\"loss:     {loss:.4f}\\naccuracy: {acc:.4f}\")"
   ],
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "loss:     2.4682\n",
      "accuracy: 0.6507\n"
     ]
    }
   ],
   "execution_count": 26
  },
  {
   "metadata": {},
   "cell_type": "code",
   "outputs": [],
   "execution_count": null,
   "source": ""
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "pytorch",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.8"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
